const _ = require("lodash");
const context = require("./../../util/context");
const pkg = require("./../../package.json");

module.exports = function(bookshelf) {
  const modelPrototype = bookshelf.Model.prototype;
  const collectionPrototype = bookshelf.Collection.prototype;

  bookshelf.Collection = bookshelf.Collection.extend({
    initialize: function() {
      collectionPrototype.initialize.call(this);

      this.on("fetching", (collection, columns, options) => {});
      this.on("counting", (collection, options) => {});
    }
  });

  bookshelf.Model = bookshelf.Model.extend({
    initialize: function() {
      modelPrototype.initialize.call(this);

      this.on("fetching", (model, columns, options) => {});
      this.on("counting", (model, options) => {});
    },

    destroy: async function(options = {}) {
      this.fetchOptions(options, true);

      if (options.withRedisKey) {
        await BaaS.redis.delAll(options.withRedisKey);
      }
      return modelPrototype.destroy.call(this, options).then(res => {
        return res ? res.serialize() : "";
      });
    },

    save: async function(options = {}) {
      this.fetchOptions(options, true);

      if (options.withRedisKey) {
        await BaaS.redis.delAll(options.withRedisKey);
      }
      return modelPrototype.save.call(this, null, options).then(res => {
        return res ? res.serialize() : "";
      });
    },

    fetchOptions: function(options = {}, ignoreDev = false) {
      const _options = this.constructor.prototype;
      if (_options.appid && _options.appkey && options.withRedisKey) {
        options.withRedisKey = context(this.constructor.prototype).getAppKey(
          options.withRedisKey,
          ignoreDev
        );
      }
    },

    fetch: async function(options = {}) {
      this.fetchOptions(options);

      if (options.withRedisKey) {
        const cache = await BaaS.redis.cache(options.withRedisKey);
        if (!_.isNil(cache)) {
          return cache;
        } else {
          return modelPrototype.fetch
            .call(this, options)
            .then(res => {
              return res ? res.serialize() : "";
            })
            .then(res => {
              BaaS.redis.cache(options.withRedisKey, res);
              return res;
            });
        }
      }
      return modelPrototype.fetch.call(this, options).then(res => {
        return res ? res.serialize() : "";
      });
    },

    fetchNoCache: async function(options = {}) {
      return modelPrototype.fetch.call(this, options);
    },

    fetchAll: async function(options = {}) {
      this.fetchOptions(options);

      if (options.withRedisKey) {
        const cache = await BaaS.redis.cache(options.withRedisKey);
        if (!_.isNil(cache)) {
          return cache;
        } else {
          return modelPrototype.fetchAll
            .call(this, options)
            .then(res => {
              return res.serialize();
            })
            .then(res => {
              BaaS.redis.cache(options.withRedisKey, res);
              return res;
            });
        }
      }
      return modelPrototype.fetchAll.call(this, options).then(res => {
        return res.serialize();
      });
    },

    fetchAllNoCache: async function(options = {}) {
      return modelPrototype.fetchAll.call(this, options);
    },

    fetchPage: async function(options = {}) {
      this.fetchOptions(options);

      if (options.withRedisKey) {
        const cache = await BaaS.redis.cache(options.withRedisKey);
        if (!_.isNil(cache)) {
          return cache;
        } else {
          return modelPrototype.fetchPage
            .call(this, options)
            .then(res => {
              return {
                data: res.serialize(),
                pagination: res.pagination
              };
            })
            .then(res => {
              BaaS.redis.cache(options.withRedisKey, res);
              return res;
            });
        }
      }
      return modelPrototype.fetchPage.call(this, options).then(res => {
        return {
          data: res.serialize(),
          pagination: res.pagination
        };
      });
    },

    fetchPageNoCache: async function(options = {}) {
      return modelPrototype.fetchPage.call(this, options);
    }
  });
};
